/**
 * MCP Server Manager
 * Manages MCP (Model Context Protocol) server lifecycle and configuration
 */

const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
const ConfigManager = require('../config/config-manager');
const { ErrorFactory, ErrorHandler } = require('../utils/errors');
const { getLogger } = require('../logging/logger');

const logger = getLogger();

/**
 * MCP Server Manager class
 * Handles MCP server lifecycle, configuration, and tool registration
 */
class McpServerManager {
  constructor(config = {}) {
    this.config = {
      name: 'vision-analyzer',
      version: '1.0.0',
      timeout: 30000,
      ...config
    };

    this.server = null;
    this.transport = null;
    this.isRunning = false;
    this.startTime = null;
    this.tools = new Map();
    this.requestHandlers = new Map();
    this.configManager = null;

    // Service metrics
    this.metrics = {
      requests: 0,
      errors: 0,
      startTime: null,
      lastRequest: null
    };

    this._initializeServer();
  }

  /**
   * Initialize MCP server instance
   * @private
   */
  _initializeServer() {
    try {
      this.server = new Server(
        {
          name: this.config.name,
          version: this.config.version,
        },
        {
          capabilities: {
            tools: {},
          },
        }
      );

      logger.info(`MCP server initialized: ${this.config.name} v${this.config.version}`);
    } catch (error) {
      throw ErrorHandler.handle(error, { operation: 'mcp_server_init' });
    }
  }

  /**
   * Start MCP server
   * @param {Object} options - Start options
   * @returns {Promise<Object>} Start result
   */
  async startServer(options = {}) {
    if (this.isRunning) {
      return {
        success: false,
        error: 'MCP server is already running',
        server: this.getServerInfo()
      };
    }

    try {
      // Initialize configuration manager
      if (!this.configManager) {
        this.configManager = new ConfigManager();
      }

      // Load configuration
      const config = await this.configManager.getConfig();
      this._updateServerConfig(config);

      // Set up transport
      const transportType = options.transport || 'stdio';
      this.transport = this._createTransport(transportType, options);

      // Connect server to transport
      await this.server.connect(this.transport);

      // Set up request handlers
      this._setupRequestHandlers();

      // Update state
      this.isRunning = true;
      this.startTime = new Date();
      this.metrics.startTime = this.startTime;

      logger.info(`MCP server started on ${transportType}`);
      logger.info(`MCP server listening: ${this.config.name} v${this.config.version}`);

      return {
        success: true,
        server: this.getServerInfo(),
        transport: transportType,
        startTime: this.startTime
      };

    } catch (error) {
      const errorInfo = ErrorHandler.handle(error, { operation: 'mcp_server_start' });
      logger.error('Failed to start MCP server', errorInfo);

      return {
        success: false,
        error: errorInfo.message || 'Failed to start MCP server',
        details: errorInfo
      };
    }
  }

  /**
   * Stop MCP server
   * @returns {Promise<Object>} Stop result
   */
  async stopServer() {
    if (!this.isRunning) {
      return {
        success: true,
        message: 'MCP server is not running'
      };
    }

    try {
      // Close transport if available
      if (this.transport && typeof this.transport.close === 'function') {
        await this.transport.close();
      }

      // Clear server state
      this.isRunning = false;
      this.transport = null;

      const uptime = this.getUptime();

      logger.info(`MCP server stopped after ${uptime}ms uptime`);

      return {
        success: true,
        uptime: uptime,
        metrics: this.getMetrics()
      };

    } catch (error) {
      const errorInfo = ErrorHandler.handle(error, { operation: 'mcp_server_stop' });
      logger.error('Failed to stop MCP server', errorInfo);

      return {
        success: false,
        error: errorInfo.message || 'Failed to stop MCP server',
        details: errorInfo
      };
    }
  }

  /**
   * Restart MCP server
   * @param {Object} options - Restart options
   * @returns {Promise<Object>} Restart result
   */
  async restartServer(options = {}) {
    logger.info('Restarting MCP server...');

    // Stop server first
    const stopResult = await this.stopServer();
    if (!stopResult.success) {
      return {
        success: false,
        error: 'Failed to stop server for restart',
        stopError: stopResult.error
      };
    }

    // Clear server instance
    this.server = null;
    this.tools.clear();
    this.requestHandlers.clear();

    // Reinitialize server
    this._initializeServer();

    // Start server with new options
    const startResult = await this.startServer(options);

    return {
      success: startResult.success,
      server: startResult.server,
      error: startResult.error,
      restartTime: new Date()
    };
  }

  /**
   * Get server status
   * @returns {Object} Server status
   */
  getServerStatus() {
    return {
      running: this.isRunning,
      name: this.config.name,
      version: this.config.version,
      uptime: this.getUptime(),
      startTime: this.startTime,
      transport: this.transport ? this.transport.constructor.name : null,
      tools: this.tools.size,
      handlers: this.requestHandlers.size,
      metrics: this.getMetrics()
    };
  }

  /**
   * Get server information
   * @returns {Object} Server info
   */
  getServerInfo() {
    return {
      name: this.config.name,
      version: this.config.version,
      capabilities: this.server ? this.server.capabilities : {},
      tools: Array.from(this.tools.keys()),
      running: this.isRunning
    };
  }

  /**
   * Get server uptime in milliseconds
   * @returns {number} Uptime in milliseconds
   */
  getUptime() {
    if (!this.startTime) {
      return 0;
    }
    return Date.now() - this.startTime.getTime();
  }

  /**
   * Get server metrics
   * @returns {Object} Metrics
   */
  getMetrics() {
    return {
      ...this.metrics,
      uptime: this.getUptime(),
      memory: process.memoryUsage(),
      toolsCount: this.tools.size,
      handlersCount: this.requestHandlers.size
    };
  }

  /**
   * Register a tool with the MCP server
   * @param {Object} tool - Tool definition
   * @returns {boolean} Registration success
   */
  registerTool(tool) {
    if (!this.server) {
      logger.warn('Cannot register tool: server not initialized');
      return false;
    }

    try {
      if (!tool.name || !tool.description || !tool.inputSchema) {
        throw ErrorFactory.validation('Invalid tool definition: missing required fields', ['tool name is required', 'tool description is required', 'tool inputSchema is required']);
      }

      this.tools.set(tool.name, tool);
      logger.debug(`Tool registered: ${tool.name}`);
      return true;

    } catch (error) {
      logger.error(`Failed to register tool: ${error.message}`);
      return false;
    }
  }

  /**
   * Get all registered tools
   * @returns {Array} Array of tool definitions
   */
  getRegisteredTools() {
    return Array.from(this.tools.values());
  }

  /**
   * Update server configuration
   * @param {Object} newConfig - New configuration
   */
  updateConfiguration(newConfig) {
    const oldConfig = { ...this.config };

    // Update configuration
    this.config = { ...this.config, ...newConfig };

    // Update server if it's running and name/version changed
    if (this.isRunning &&
        (oldConfig.name !== this.config.name || oldConfig.version !== this.config.version)) {
      logger.warn('Server name or version changed while running. Restart required.');
    }

    logger.info('MCP server configuration updated');
  }

  /**
   * Create transport based on type
   * @param {string} type - Transport type
   * @param {Object} options - Transport options
   * @returns {Object} Transport instance
   * @private
   */
  _createTransport(type, options = {}) {
    switch (type) {
      case 'stdio':
        return new StdioServerTransport();

      case 'http':
        // For future HTTP transport support
        throw ErrorFactory.configuration('HTTP transport not yet implemented', { transport: 'http', status: 'not_implemented' });

      default:
        throw ErrorFactory.configuration(`Unsupported transport type: ${type}`, { transport: type, supported: ['stdio'] });
    }
  }

  /**
   * Update server configuration from config manager
   * @param {Object} config - Configuration object
   * @private
   */
  _updateServerConfig(config) {
    if (config.mcp) {
      this.updateConfiguration({
        name: config.mcp.server_name || this.config.name,
        version: config.mcp.server_version || this.config.version,
        timeout: config.mcp.timeout || this.config.timeout
      });
    }
  }

  /**
   * Set up request handlers
   * @private
   */
  _setupRequestHandlers() {
    // This would set up the actual MCP request handlers
    // For now, it's a placeholder for future implementation
    logger.debug('MCP request handlers set up');
  }

  /**
   * Handle tool calls
   * @param {string} toolName - Tool name
   * @param {Object} args - Tool arguments
   * @returns {Promise<Object>} Tool result
   */
  async handleToolCall(toolName, args = {}) {
    if (!this.isRunning) {
      throw ErrorFactory.mcp('MCP server is not running', 'tool-call', { toolName, args });
    }

    this.metrics.requests++;
    this.metrics.lastRequest = new Date();

    try {
      // Check if tool is registered
      if (!this.tools.has(toolName)) {
        throw ErrorFactory.mcp(`Unknown tool: ${toolName}`, 'tool-call', { toolName, availableTools: Array.from(this.tools.keys()) });
      }

      // This would dispatch to the actual tool handler
      // For now, return a placeholder response
      logger.debug(`Tool called: ${toolName}`, args);

      return {
        success: true,
        tool: toolName,
        result: `Tool ${toolName} executed successfully`,
        timestamp: new Date()
      };

    } catch (error) {
      this.metrics.errors++;
      logger.error(`Tool call failed: ${toolName}`, error);

      return {
        success: false,
        tool: toolName,
        error: error.message,
        timestamp: new Date()
      };
    }
  }

  /**
   * Get MCP server instance
   * @returns {Server|null} MCP server instance
   */
  getMcpServer() {
    return this.server;
  }

  /**
   * Check if server is healthy
   * @returns {boolean} Health status
   */
  isHealthy() {
    return this.isRunning && this.server !== null;
  }
}

module.exports = McpServerManager;